{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fbb1824a-cd01-4258-8add-d0773ae69fcc",
   "metadata": {},
   "source": [
    "## Neo4j Langchain Ollama"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b0532eb7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip3 install --upgrade --quiet  langchain langchain-community langchain-openai neo4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "725570a9-7813-4bbc-bce3-e713f2a66bc3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple/\n",
      "Collecting neo4j==5.19.0\n",
      "  Downloading https://mirrors.ustc.edu.cn/pypi/packages/c3/cf/4d879894f063f9845bd3573cd92517481f470ba27c062cbe5d0e8de69769/neo4j-5.19.0.tar.gz (202 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m203.0/203.0 kB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
      "\u001b[?25h  Installing build dependencies ... \u001b[?25ldone\n",
      "\u001b[?25h  Getting requirements to build wheel ... \u001b[?25ldone\n",
      "\u001b[?25h  Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n",
      "\u001b[?25hRequirement already satisfied: pytz in /opt/conda/lib/python3.11/site-packages (from neo4j==5.19.0) (2023.3.post1)\n",
      "Building wheels for collected packages: neo4j\n",
      "  Building wheel for neo4j (pyproject.toml) ... \u001b[?25ldone\n",
      "\u001b[?25h  Created wheel for neo4j: filename=neo4j-5.19.0-py3-none-any.whl size=280741 sha256=e0bce0c89e96e736404c736b7281fa70644fc0f056e5210b833a1d893dcbf249\n",
      "  Stored in directory: /home/jovyan/.cache/pip/wheels/82/66/fc/e38de440e7b7013f4169f3ed1799ed9c7df326de70e84be3b5\n",
      "Successfully built neo4j\n",
      "Installing collected packages: neo4j\n",
      "Successfully installed neo4j-5.19.0\n"
     ]
    }
   ],
   "source": [
    "!pip install neo4j==5.19.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8667949b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: langchain\n",
      "Version: 0.3.7\n",
      "Summary: Building applications with LLMs through composability\n",
      "Home-page: https://github.com/langchain-ai/langchain\n",
      "Author: \n",
      "Author-email: \n",
      "License: MIT\n",
      "Location: /opt/conda/lib/python3.11/site-packages\n",
      "Requires: aiohttp, langchain-core, langchain-text-splitters, langsmith, numpy, pydantic, PyYAML, requests, SQLAlchemy, tenacity\n",
      "Required-by: langchain-community\n",
      "---\n",
      "Name: neo4j\n",
      "Version: 5.19.0\n",
      "Summary: Neo4j Bolt driver for Python\n",
      "Home-page: \n",
      "Author: \n",
      "Author-email: \"Neo4j, Inc.\" <drivers@neo4j.com>\n",
      "License: Apache License, Version 2.0\n",
      "Location: /opt/conda/lib/python3.11/site-packages\n",
      "Requires: pytz\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "!pip show langchain neo4j"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34de1292-1534-4251-b323-9a6775aae5a9",
   "metadata": {},
   "source": [
    "## Create Neo4jGraph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9ad9fb1b-d5c6-4e4e-95f3-e7d731afcf96",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"NEO4J_URI\"] = \"bolt://neo4j:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"neo4j123\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b87033b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.graphs import Neo4jGraph\n",
    "# graph = Neo4jGraph(url=\"bolt://neo4j:7687\", username=\"neo4j\", password=\"neo4j123\")\n",
    "\n",
    "graph = Neo4jGraph()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "af49ef62-2ce6-4cbb-b854-9a018fb4f00a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'person.name': 'Personal Banking Customer',\n",
       "  'person.description': 'A customer of the bank, with personal bank accounts.'},\n",
       " {'person.name': 'Customer Service Staff',\n",
       "  'person.description': 'Customer service staff within the bank.'},\n",
       " {'person.name': 'Back Office Staff',\n",
       "  'person.description': 'Administration and support staff within the bank.'}]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.query(\"MATCH (person:Person) RETURN person.name,person.description LIMIT 10\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0096ae85-b723-48ab-a463-16e86c012f33",
   "metadata": {},
   "source": [
    "## Create OllamaLLM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "66510f86-9540-4697-b85b-f9a7cf22bec2",
   "metadata": {},
   "outputs": [],
   "source": [
    "ollama_url = \"http://ollama:11434\"\n",
    "# ollama_url = \"http://localhost:11434\"\n",
    "\n",
    "model_name = \"mistral:v0.3\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "333bcd81-9ca5-4a13-bc5b-51baa85d6139",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_ollama import OllamaLLM\n",
    "\n",
    "llm = OllamaLLM(base_url=ollama_url,model=model_name)\n",
    "\n",
    "# from langchain.chains import GraphCypherQAChain\n",
    "# # from langchain_openai import ChatOpenAI\n",
    "# # llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n",
    "# import utility\n",
    "# from langchain_mistralai.chat_models import ChatMistralAI\n",
    "# llm = ChatMistralAI(mistral_api_key=utility.get_env(\"MISTRAL_API_KEY\",None), model=\"open-mistral-7b\", temperature=0)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60c8aaee-2973-48ed-bb2a-f6981cbd888a",
   "metadata": {},
   "source": [
    "## Create GraphCypherQAChain\n",
    "\n",
    "Set `allow_dangerous_requests=True` to avoid error\n",
    "```log\n",
    "ValueError: In order to use this chain, you must acknowledge that it can make dangerous requests by setting `allow_dangerous_requests` to `True`.You must narrowly scope the permissions of the database connection to only include necessary permissions. Failure to do so may result in data corruption or loss or reading sensitive data if such data is present in the database.Only use this chain if you understand the risks and have taken the necessary precautions. See https://python.langchain.com/docs/security for more information.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "dd6c9c3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import GraphCypherQAChain\n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True, allow_dangerous_requests=True)\n",
    "\n",
    "\n",
    "# top_k=2\n",
    "# return_intermediate_steps=True\n",
    "# return_direct=True\n",
    "\n",
    "# from langchain_core.prompts import ChatPromptTemplate\n",
    "# from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "# prompt = ChatPromptTemplate.from_template(\"What software systems are used by Back Office Staff?\")\n",
    "\n",
    "# from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "# output_parser = StrOutputParser()\n",
    "\n",
    "\n",
    "# chain = llm | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d9b3782f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3m MATCH (person:Person {name:'Back Office Staff'})-[:Uses]->(system:SoftwareSystem)\n",
      "    RETURN system.name\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'system.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Back Office Staff?',\n",
       " 'result': ' The Mainframe Banking System is the software system used by Back Office Staff.'}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Back Office Staff?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f46f7b1f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3m MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "   WHERE p.name = \"Personal Banking Customer\"\n",
      "   RETURN ss.name\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Personal Banking Customer?',\n",
       " 'result': ' The software systems used by a Personal Banking Customer are ATM and Internet Banking System.'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Personal Banking Customer?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "07b0ce89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.name =~ 'Bank Staff'\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Bank Staff?',\n",
       " 'result': \"I'm unable to provide an answer as the given information does not specify the software systems used by Bank Staff.\"}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Bank Staff?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "11c55796",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS 'Bank Staff'\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'Mainframe Banking System'}, {'ss.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': \"What software systems are used by Bank Staff? Please check whether the 'tags' properties in Person contains the person \",\n",
       " 'result': \"The information provided indicates that 'Mainframe Banking System' is mentioned twice. Therefore, the answer would be:\\n\\nHelpful Answer: Bank staff use Mainframe Banking Systems.\"}"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Bank Staff? Please check whether the 'tags' properties in Person contains the person \"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "ca561b50",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS 'Customer' AND ss.tags IS NOT NULL\n",
      "RETURN ss.name, ss.description\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM', 'ss.description': 'Allows customers to withdraw cash.'}, {'ss.name': 'Internet Banking System', 'ss.description': 'Allows customers to view information about their bank accounts, and make payments.'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': \"What software systems are used by customers? Use WHERE clause to check whether the value of 'tags' property contains 'Customer\",\n",
       " 'result': \"The ATM and Internet Banking System are the software systems used by customers. To check if a specific system is used by customers with the 'Customer' tag, you can use a WHERE clause with the 'tags' property. For example:\\n\\n```cypher\\nMATCH (s:SoftwareSystem {name: 'ATM'})-[:USED_BY]->(c:Customer)\\nWHERE c.tags CONTAINS 'Customer'\\nRETURN s.name AS software_system\\n\\nMATCH (s:SoftwareSystem {name: 'Internet Banking System'})-[:USED_BY]->(c:Customer)\\nWHERE c.tags CONTAINS 'Customer'\\nRETURN s.name AS software_system\\n```\\n\\nThis query will return the names of the software systems that are used by customers with the 'Customer' tag.\"}"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by customers? Use WHERE clause to check whether the value of 'tags' property contains 'Customer\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "214f2f09",
   "metadata": {},
   "source": [
    "## Few-shot examples\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abb2c24f",
   "metadata": {},
   "outputs": [],
   "source": [
    "examples = [\n",
    "    {\n",
    "        \"question\": \"List all the users\",\n",
    "        \"query\": \"MATCH (p:Person) RETURN distinct p\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by customer\",\n",
    "        \"query\": \"MATCH (p:Person)-[r]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' RETURN DISTINCT s.name,s.description\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by staff\",\n",
    "        \"query\": \"MATCH (p:Person)-[r]->(s:SoftwareSystem) WHERE p.tags contains 'Bank Staff' RETURN DISTINCT s.name,s.description\",\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abeedf5c-8735-4419-9a08-324dd5554dc8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "\n",
    "example_prompt = PromptTemplate.from_template(\n",
    "    \"Question: {question}\\nCypher query: {query}\"\n",
    ")\n",
    "# prompt = FewShotPromptTemplate(\n",
    "#     examples=examples,\n",
    "#     example_prompt=example_prompt,\n",
    "#     prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nHere is the schema information\\n{schema}.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n",
    "#     suffix=\"Question: {question}\\nCypher query: \",\n",
    "#     input_variables=[\"question\", \"schema\"],\n",
    "# )\n",
    "prompt = FewShotPromptTemplate(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n",
    "    suffix=\"Question: {question}\\nCypher query: \",\n",
    "    input_variables=[\"question\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ae5b263",
   "metadata": {},
   "outputs": [],
   "source": [
    "# print(prompt.format(question=\"What software systems are used by Staff?\", schema=\"neo4j\"))\n",
    "print(prompt.format(question=\"What software systems are used by Staff?\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f9926c14",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, cypher_prompt=prompt, verbose=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4a2d0bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "response = chain.invoke({\"query\": \"What software systems are used by customer?\"})\n",
    "response\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
